// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
const db = cloud.database({
  throwOnNotFound: false
})
const _ = db.command
const $ = db.command.aggregate

function text2date(dateRange) {
  return [
    new Date(dateRange[0]).getTime(),
    new Date(dateRange[1]).getTime(),
  ];
}

async function addManyAppointments({ stadium_id, dateRange, timeRange }) {
  const res = await db.collection('tb_court').aggregate()
    .match({ stadium_id })
    .limit(200)
    .end();
  const courtList = res.list;
  dateRange = text2date(dateRange);
  // 时间数组设置
  let validNum = 0;
  const tmpArr = Array.apply(null, { length: 48 }).map(v => 0);
  const l = parseInt(timeRange[0]) * 60 + parseInt(timeRange[0].substr(3));
  const r = parseInt(timeRange[1]) * 60 + parseInt(timeRange[1].substr(3));
  for (let t = l; t <= r; t += 30) tmpArr[t / 30] = 3, ++validNum;
  // 事务执行
  const transaction = await db.startTransaction();
  const failList = [];
  for (let date = dateRange[0]; date <= dateRange[1]; date += 86400000) {
    const appointmentId = stadium_id + '-' + parseInt(date / 100000); // 足以区分预约 stadium-day
    const res = await transaction.collection('tb_appointment').doc(appointmentId).get();
    const sku_valid = {}, sku_state = {}, sku_remark = {};
    if (!res.data) {
      courtList.forEach(o => {
        sku_valid[o._id] = validNum;
        sku_state[o._id] = tmpArr;
        sku_remark[o._id] = tmpArr.map(v => '');
      });
      await transaction.collection('tb_appointment').doc(appointmentId).set({
        data: { stadium_id, date_ts: date, sku_valid, sku_state, sku_remark }
      })
    } else {
      failList.push(date);
    }
  }
  await transaction.commit();
  return { errMsg: '', list: failList };
}

async function searchStadiumList(searchText, searchData, dateRange) {
  // dateRange = ['yyyy-MM-dd', 'yyyy-MM-dd']
  dateRange = text2date(dateRange);
  searchData.date_ts = _.and([_.gte(dateRange[0]), _.lte(dateRange[1])]);
  const res = await db.collection('tb_appointment').aggregate()
    .match(searchData)
    .lookup({
      from: 'tb_stadium',
      localField: 'stadium_id',
      foreignField: '_id',
      as: 'stadium'
    })
    .replaceRoot({
      newRoot: $.mergeObjects([$.arrayElemAt(['$stadium', 0]), '$$ROOT']),
    })
    .project({
      stadium_id: 1, date_ts: 1, stadium_name: 1, appointment_period: 1,
      sku_valid_cnt: $.let({
        vars: {
          courtValid: $.objectToArray('$sku_valid'),
        },
        in: $.sum('$$courtValid.v')
      }),
    })
    .match({ stadium_name: new RegExp(searchText, 'i') })
    .limit(200)
    .sort({ date_ts: 1 })
    .end();
  return res.list;
}

async function findOne(appointmentId) {
  const res = await db.collection('tb_appointment').aggregate()
    .match({ _id: appointmentId })
    .project({
      sku_valid: 1, sku_state: 1, sku_remark: 1,
      court_ids: $.let({
        vars: {
          courts: $.objectToArray('$sku_valid')
        },
        in: '$$courts.k'
      }),
      stadium_id: $.let({
        vars: {
          stadium_id: $.substrBytes(['$_id', 0, $.indexOfBytes(['$_id', '-'])]),
        },
        in: '$$stadium_id'
      })
    })
    .lookup({
      from: 'tb_court',
      localField: 'court_ids',
      foreignField: '_id',
      as: 'courts',
    })
    .lookup({
      from: 'tb_stadium',
      localField: 'stadium_id',
      foreignField: '_id',
      as: 'stadium',
    })
    .end();
  return res.list;
}

async function filterOne(appointmentId, sportId) {
  const res = await db.collection('tb_appointment').aggregate()
    .match({ _id: appointmentId })
    .project({
      sku_valid: 1, sku_state: 1, sku_remark: 1,
      court_ids: $.let({
        vars: {
          courts: $.objectToArray('$sku_valid')
        },
        in: '$$courts.k'
      })
    })
    .lookup({
      from: 'tb_court',
      localField: 'court_ids',
      foreignField: '_id',
      as: 'courts',
    })
    .project({
      sku_valid: $.objectToArray('$sku_valid'),
      sku_state: $.objectToArray('$sku_state'),
      sku_remark: $.objectToArray('$sku_remark'),
      courts: $.filter({
        input: '$courts',
        as: 'item',
        cond: $.eq(['$$item.sport_id', sportId])
      }),
      court_ids: 1
    })
    .project({
      sku_valid: $.filter({
        input: '$sku_valid',
        cond: $.in(['$$this.k', '$courts._id'])
      }),
      sku_state: $.filter({
        input: '$sku_state',
        cond: $.in(['$$this.k', '$courts._id'])
      }),
      sku_remark: $.filter({
        input: '$sku_remark',
        cond: $.in(['$$this.k', '$courts._id'])
      }),
      court_ids: $.filter({
        input: '$court_ids',
        cond: $.in(['$$this', '$courts._id'])
      }),
      courts: 1,
    })
    .project({
      sku_valid: $.arrayToObject('$sku_valid'),
      sku_state: $.arrayToObject('$sku_state'),
      sku_remark: $.arrayToObject('$sku_remark'),
      courts: 1,
      stadium_id: $.let({
        vars: {
          stadium_id: $.substrBytes(['$_id', 0, $.indexOfBytes(['$_id', '-'])]),
        },
        in: '$$stadium_id'
      })
    })
    .lookup({
      from: 'tb_stadium',
      localField: 'stadium_id',
      foreignField: '_id',
      as: 'stadium',
    })
    .end();
  return res.list;
}

// 锁场
async function lockAppointment(appointmentId, skuList) {
  // 事务锁
  const transaction = await db.startTransaction();
  const res = await transaction.collection('tb_appointment').doc(appointmentId).get()
  for (let i = 0; i < skuList.length; i++) {
    if (res.data.sku_state[skuList[i].court_id][skuList[i].rid] == 3) {
      await transaction.collection('tb_appointment').doc(appointmentId).update({
        data: {
          [`sku_state.${skuList[i].court_id}.${skuList[i].rid}`]: _.inc(-1),
          [`sku_valid.${skuList[i].court_id}`]: _.inc(-1)
        }
      })
    } else {
      await transaction.rollback();
      return { errMsg: '锁场失败，请刷新界面' }
    }
  }
  await transaction.commit();
  return { errMsg: '' };
}


// 预约变更
async function changeStatus(order_id, status) {
  const res = await db.collection('tb_order').doc(order_id).get();
  const updData = {};
  res.data.sku_list.forEach(o => {
    updData[`sku_state.${o.court_id}.${o.rid}`] = status;
  })
  return await db.collection('tb_appointment').doc(res.data.appointment_id).update({ data: updData });
}

// 云函数入口函数
exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()

  let errMsg = ''
  if (event.funcName == 'insert') {
  } else if (event.funcName == 'query') {
    return await searchStadiumList(event.searchText, event.searchData, event.dateRange);
  } else if (event.funcName == 'queryOne') {
    if (typeof (event.sport_id) !== 'undefined' && parseInt(event.sport_id) !== -1) {
      return await filterOne(event._id, event.sport_id);
    } else {
      return await findOne(event._id);
    }
  } else if (event.funcName == 'update') {
  } else if (event.funcName == 'insertMany') {
    return await addManyAppointments(event.addData);
  } else if (event.funcName == 'lockAppointment') {
    return await lockAppointment(event.appointmentId, event.skuList)
  } else if (event.funcName == 'changeStatus') {
    return await changeStatus(event.order_id, event.status);
  }
  return {
    event,
    errMsg,
    // openid: wxContext.OPENID,
    // appid: wxContext.APPID,
    // unionid: wxContext.UNIONID,
  }
}